import os
import shutil
import subprocess
import random
from datetime import datetime
from src.modules.duration_adjuster import DurationAdjuster
from src.modules.fade_processor import FadeProcessor
from src.modules.file_manager import FileManager
from src.utils.logger import log_info, log_error
from src.modules.media_processor import MediaProcessor

class VideoMerger:
    """专门处理视频合并、转换和添加过渡效果的类"""
    
    def __init__(self, transition_effect, transition_duration, startupinfo=None,
                 logger = None,
                 ):
        """
        初始化VideoMerger
        
        :param startupinfo: 用于subprocess的startupinfo对象，默认为None
        :param transition_effect: 默认的过渡效果类型
        :param transition_duration: 默认的过渡效果持续时间（秒）
        """
        
        self.startupinfo = startupinfo
        self.file_manager = FileManager(logger=logger)
        self.transition_effect = transition_effect
        self.transition_duration = transition_duration
        self.logger = logger

    def _log_error(self, message):
        self.logger(message, level="error") if self.logger else log_error(message)
        
    
    def cleanup(self):
        """清理所有临时文件和目录"""
        self.file_manager.cleanup_category(f"video_merger")
    
    def _validate_video_paths(self, *video_paths):
        """验证视频文件路径是否存在"""
        if not video_paths:
            raise ValueError("至少需要一个视频文件路径")
        
        for path in video_paths:
            if not os.path.exists(path):
                raise FileNotFoundError(f"视频文件不存在: {path}")
        
        return True
    
    def _convert_video_to_ts(self, video_path, target_width=None, target_height=None):
        """将单个视频转换为TS格式"""
        temp_file = self.file_manager.create_temp_file(
            prefix="ts_",
            suffix=".ts",
        )
        output_ts_path = temp_file.path

        cmd = [
            "ffmpeg",
            "-i", video_path,
            "-vf", f"fps=30,scale={target_width}:{target_height}:force_original_aspect_ratio=decrease,pad={target_width}:{target_height}:(ow-iw)/2:(oh-ih)/2",
            "-c:v", "libx264",
            "-preset", "veryfast",
            "-crf", "23",
            "-c:a", "aac",
            "-b:a", "192k",
            "-f", "mpegts",
            "-y",
            output_ts_path
        ]
        log_info(f"[INFO] 转码为 TS: {os.path.basename(video_path)} -> {target_width}x{target_height}")
        subprocess.run(cmd, check=True, startupinfo=self.startupinfo)
        return output_ts_path

    def _normalize_video(self, video_path, output_path=None, target_width=None, target_height=None):
        """标准化视频为统一格式和分辨率"""
        if output_path is None:
            temp_dir = self.file_manager.create_temp_dir().path
            os.makedirs(temp_dir, exist_ok=True)
            output_path = os.path.join(temp_dir, f"normalized_{random.randint(1000, 9999)}.mp4")
        
        cmd = [
            "ffmpeg",
            "-i", video_path,
            "-vf", f"fps=30,scale={target_width}:{target_height}:force_original_aspect_ratio=decrease,pad={target_width}:{target_height}:(ow-iw)/2:(oh-ih)/2",
            "-c:v", "libx264",
            "-preset", "veryfast",
            "-crf", "23",
            "-c:a", "aac",
            "-b:a", "192k",
            "-y",
            output_path
        ]
        log_info(f"[INFO] 标准化视频 {os.path.basename(video_path)} 到 {target_width}x{target_height}")
        subprocess.run(cmd, check=True, startupinfo=self.startupinfo)
        return output_path
 
    def _merge_ts_files(self, ts_files, output_path):
        """合并多个TS文件"""
        concat_str = "|".join(ts_files)
        merge_cmd = [
            "ffmpeg",
            "-i", f"concat:{concat_str}",
            "-c", "copy",
            "-bsf:a", "aac_adtstoasc",
            "-y",
            output_path
        ]
        log_info(f"[INFO] 开始合并 TS: {' '.join(merge_cmd)}")
        subprocess.run(merge_cmd, check=True, startupinfo=self.startupinfo)
        log_info(f"[INFO] 合并完成: {output_path}")
        return output_path
    
    def separate_audio_from_video(self, video_path, output_path=None):
        """
        使用 demucs 分离视频中的人声，创建一个没有人声的视频版本。
        
        Args:
            video_path: 输入视频文件路径
            output_path: 输出视频路径（如果为None，将自动生成）
        
        Returns:
            str: 处理后的视频文件路径
        """
        if not os.path.exists(video_path):
            raise FileNotFoundError(f"视频文件不存在: {video_path}")
        
        # 如果未提供输出路径，生成默认路径
        if output_path is None:
            base_dir = os.path.dirname(video_path)
            filename = os.path.basename(video_path)
            name, ext = os.path.splitext(filename)
            output_path = os.path.join(base_dir, f"{name}_novocals{ext}")
        
        # 使用ASCII字符创建临时目录，避免路径中包含非ASCII字符
        # 使用系统临时目录而非视频所在目录
        temp_dir = self.file_manager.create_temp_dir().path
        os.makedirs(temp_dir, exist_ok=True)
        
        # 从视频中提取音频
        audio_path = os.path.join(temp_dir, "audio.wav")
        
        try:
            log_info(f"开始提取音频: {video_path}")
            cmd = ["ffmpeg", "-i", video_path, "-q:a", "0", audio_path, "-y"]
            subprocess.run(cmd, startupinfo=self.startupinfo, stderr=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8')
            
            # 使用 demucs 处理音频分离人声
            log_info(f"使用 demucs 分离人声")
            demucs_output_dir = os.path.join(temp_dir, "separated")
            os.makedirs(demucs_output_dir, exist_ok=True)
            
            # 使用简单的模型选项
            demucs_cmd = [
                "demucs", 
                "--out", demucs_output_dir,
                "--two-stems=vocals",
                "--mp3",  # 使用mp3格式避免某些wav格式兼容性问题
                audio_path
            ]
            
            # 处理字节输出而不是字符串
            process = subprocess.run(demucs_cmd, startupinfo=self.startupinfo, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
            
            if process.returncode != 0:
                # 不对stderr进行解码，避免Unicode错误
                self._log_error(f"Demucs命令执行失败，返回代码: {process.returncode}")
                
                # 尝试另一种方法 - 使用--filename参数直接指定输出名称
                log_info("尝试备用分离方法...")
                # 直接使用原始视频作为输出
                log_info(f"跳过音频分离，返回原始视频: {video_path}")
                if output_path != video_path:
                    shutil.copy(video_path, output_path)
                return output_path
            
            # 递归搜索无人声文件
            no_vocals_path = None
            for root, _, files in os.walk(demucs_output_dir):
                for file in files:
                    if ("no_vocals" in file.lower() or "instrumental" in file.lower()) and file.endswith((".wav", ".mp3")):
                        no_vocals_path = os.path.join(root, file)
                        log_info(f"找到无人声文件: {no_vocals_path}")
                        break
                if no_vocals_path:
                    break
            
            if not no_vocals_path:
                self._log_error("未能找到无人声音频文件")
                # 如果无法找到，返回原始视频
                log_info(f"跳过音频分离，返回原始视频: {video_path}")
                if output_path != video_path:
                    shutil.copy(video_path, output_path)
                return output_path
            
            log_info(f"重新合成视频与无人声音频: {no_vocals_path}")
            # 将无人声音频与原始视频合并
            cmd = [
                "ffmpeg", 
                "-i", video_path,  # 原始视频
                "-i", no_vocals_path,  # 无人声音频
                "-c:v", "copy",  # 不重新编码视频流
                "-map", "0:v:0",  # 使用第一个输入的视频
                "-map", "1:a:0",  # 使用第二个输入的音频
                "-shortest",  # 当最短的输入结束时结束
                "-y",
                output_path
            ]
            
            subprocess.run(cmd, startupinfo=self.startupinfo, stderr=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf-8')
            
            # Replace original video with no-vocals version
            if os.path.exists(output_path) and os.path.exists(video_path):
                try:
                    os.remove(video_path)
                    shutil.move(output_path, video_path)
                    output_path = video_path
                except Exception as e:
                    self._log_error(f"替换原始视频时出错: {e}")
            log_info(f"成功创建无人声视频: {output_path}")
            return output_path
            
        except Exception as e:
            self._log_error(f"分离人声时出错: {e}")
            # 出错时返回原始视频
            if video_path != output_path and output_path is not None:
                try:
                    shutil.copy(video_path, output_path)
                    log_info(f"发生错误，返回原始视频副本: {output_path}")
                    return output_path
                except Exception:
                    pass
            return video_path

    def merge_videos_with_transitions(self, *video_paths, output_path=None, remove_voice=False):
        """
        使用filter_complex一步完成多视频合并和过渡效果
        
        :param video_paths: 要合并的视频路径
        :param output_path: 输出视频路径
        :param remove_voice: 是否移除人声
        :return: 输出视频路径
        """
        media_processor = MediaProcessor(self.startupinfo, logger=self.logger)
        
        merge_method = "unknown"  # 追踪使用的合并方法
        transition_applied = False  # 追踪是否应用了过渡效果
        filter_complex = ""  # 存储filter_complex表达式
        
        try:
            # 验证输入和设置输出路径
            self._validate_video_paths(*video_paths)
            base_dir = os.path.dirname(video_paths[0])
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_path = output_path or os.path.join(base_dir, f"merged_{timestamp}.mp4")
            
            # 单个视频直接处理
            if len(video_paths) == 1:
                log_info("[转场合并] 只有一个视频，直接复制")
                shutil.copy(video_paths[0], output_path)
                merge_method = "direct_copy"
                if remove_voice:
                    output_path = self.separate_audio_from_video(output_path)
                transition_applied = True
                return transition_applied
            
            # 开始记录处理过程
            log_info(f"[转场合并] 开始合并 {len(video_paths)} 个视频", highlight=True)
            log_info(f"[转场合并] 过渡效果: {self.transition_effect}, 持续时间: {self.transition_duration}秒")
            
            # 获取每个视频的详细信息，包括分辨率、时长和音频信息
            video_durations = []
            has_audio = []
            resolutions = []
            log_info("[转场合并] 获取视频详细信息...")
            
            # 遍历所有视频获取信息
            for i, video_path in enumerate(video_paths):
                # 使用MediaLogger获取视频分辨率
                resolution = media_processor.get_video_resolution(video_path)
                if resolution:
                    resolutions.append(resolution)
                    log_info(f"[转场合并] 视频 #{i+1} 分辨率: {resolution[0]}x{resolution[1]}")
                else:
                    raise ValueError(f"无法获取视频 #{i+1} 分辨率")
                
                # 获取视频时长
                duration = media_processor.get_media_duration(video_path)
                if duration is not None:
                    video_durations.append(duration)
                    log_info(f"[转场合并] 视频 #{i+1} 时长: {duration}秒")
                    
                    # 检查过渡效果是否适合视频长度
                    if self.transition_effect and duration < self.transition_duration * 1.5:
                        log_info(f"[转场合并] 警告: 视频 #{i+1} 时长({duration}秒)不足以支持 {self.transition_duration}秒 的过渡")
                else:
                    raise ValueError(f"无法获取视频 #{i+1} 时长")
                
                # 检查音频流
                has_audio_stream = media_processor.has_audio_stream(video_path)
                has_audio.append(has_audio_stream)
                log_info(f"[转场合并] 视频 #{i+1} 音频: {'有' if has_audio_stream else '无'}")
                
            # 计算最大分辨率作为目标分辨率
            if resolutions:
                target_width = max(res[0] for res in resolutions)
                target_height = max(res[1] for res in resolutions)
                log_info(f"[转场合并] 目标分辨率设置为: {target_width}x{target_height} (最大宽高)")
            else:
                raise ValueError("没有有效的视频分辨率信息")

            
            for i, video_path in enumerate(video_paths):
                # 获取视频信息
                probe_cmd = [
                    "ffprobe", 
                    "-v", "error", 
                    "-select_streams", "v:0", 
                    "-show_entries", "stream=codec_name,width,height,r_frame_rate", 
                    "-of", "csv=p=0", 
                    video_path
                ]
                try:
                    info = subprocess.check_output(probe_cmd, startupinfo=self.startupinfo).decode().strip()
                    log_info(f"[转场合并] 视频 #{i+1}: {os.path.basename(video_path)} - {info}")
                except:
                    log_error(f"[转场合并] 无法获取视频 #{i+1} 信息: {video_path}")
                    self._log_error(f"[转场合并] 无法获取视频 #{i+1} 信息: {video_path}")

                # 获取视频时长
                duration_cmd = [
                    "ffprobe", 
                    "-v", "error", 
                    "-show_entries", "format=duration", 
                    "-of", "csv=p=0", 
                    video_path
                ]
                try:
                    duration = float(subprocess.check_output(duration_cmd, startupinfo=self.startupinfo).decode().strip())
                    video_durations.append(duration)
                    log_info(f"[转场合并] 视频 #{i+1} 时长: {duration}秒")
                    
                    # 检查过渡效果是否适合视频长度
                    if self.transition_effect and duration < self.transition_duration * 1.5:
                        log_info(f"[转场合并] 警告: 视频 #{i+1} 时长({duration}秒)不足以支持 {self.transition_duration}秒 的过渡")
                except:
                    # 设置默认时长，防止后续处理出错
                    video_durations.append(10.0)  # 假设10秒
                    self._log_error(f"[转场合并] 无法获取视频 #{i+1} 时长，使用默认值")
                
                # 检查音频流
                audio_cmd = [
                    "ffprobe", 
                    "-v", "error", 
                    "-select_streams", "a:0", 
                    "-show_entries", "stream=codec_name", 
                    "-of", "csv=p=0", 
                    video_path
                ]
                try:
                    audio_info = subprocess.check_output(audio_cmd, startupinfo=self.startupinfo).decode().strip()
                    has_audio.append(len(audio_info) > 0)
                    log_info(f"[转场合并] 视频 #{i+1} 音频: {'有' if has_audio[-1] else '无'}")
                except:
                    has_audio.append(False)
                    log_info(f"[转场合并] 视频 #{i+1} 音频: 检测失败，假设无音频")
            
            # 标准化所有视频为相同格式
            log_info("[转场合并] 标准化视频格式...")
            normalized_videos = []
            for i, video_path in enumerate(video_paths):
                log_info(f"[转场合并] 标准化视频 #{i+1}: {os.path.basename(video_path)}")
                norm_path = self._normalize_video(video_path, target_height=target_height, target_width=target_width)
                normalized_videos.append(norm_path)
            
            # 使用filter_complex构建命令
            cmd = ["ffmpeg"]
            
            # 添加所有输入
            for video_path in normalized_videos:
                cmd.extend(["-i", video_path])
            
            # 为每个视频添加标准化处理 - 详细记录filter构建过程
            log_info("[转场合并] 构建filter_complex表达式...")
            
            # 为每个视频添加标准化处理 - 保留原始宽高比
            for i in range(len(normalized_videos)):
                # 视频处理 - 保持原始比例并居中填充
                filter_part = f"[{i}:v]fps=30,scale={target_width}:{target_height}:force_original_aspect_ratio=decrease,pad={target_width}:{target_height}:(ow-iw)/2:(oh-ih)/2,setsar=1:1,format=yuv420p[v{i}];"
                filter_complex += filter_part
                log_info(f"[转场合并] 视频 #{i+1} 滤镜: {filter_part}")
                
                # 音频处理部分保持不变
                if has_audio[i]:
                    audio_part = f"[{i}:a]aformat=sample_rates=44100:channel_layouts=stereo[a{i}];"
                    filter_complex += audio_part
                    log_info(f"[转场合并] 音频 #{i+1} 滤镜: {audio_part}")
                else:
                    # 为没有音频的视频生成静音轨道
                    audio_part = f"anullsrc=r=44100:cl=stereo:d={video_durations[i]}[a{i}];"
                    filter_complex += audio_part
                    log_info(f"[转场合并] 视频 #{i+1} 无音频，添加静音轨道: {audio_part}")

            # 使用过渡效果进行合并
            if self.transition_effect and len(normalized_videos) >= 2:
                log_info("[转场合并] 添加过渡效果滤镜...")
                
                # 处理第一对视频 - 动态计算offset
                # offset应该位于第一个视频结束前，留出足够的过渡时间
                first_offset = max(0, video_durations[0] - self.transition_duration)
                first_transition = f"[v0][v1]xfade=transition={self.transition_effect}:duration={self.transition_duration}:offset={first_offset}[vtmp1];"
                filter_complex += first_transition
                log_info(f"[转场合并] 第1个过渡: {first_transition} (offset={first_offset}秒)")
                
                # 累计视频长度，用于计算后续过渡的offset
                cumulative_duration = video_durations[0] + video_durations[1] - self.transition_duration
                
                # 处理剩余视频
                for i in range(2, len(normalized_videos)):
                    # 动态计算offset：累计时长减去过渡时间
                    next_offset = max(0, cumulative_duration - self.transition_duration)
                    next_transition = f"[vtmp{i-1}][v{i}]xfade=transition={self.transition_effect}:duration={self.transition_duration}:offset={next_offset}[vtmp{i}];"
                    filter_complex += next_transition
                    log_info(f"[转场合并] 第{i}个过渡: {next_transition} (offset={next_offset}秒)")
                    
                    # 更新累计时长
                    cumulative_duration += video_durations[i] - self.transition_duration
                
                # 处理音频 - 合并所有音频流
                audio_streams = "".join([f"[a{i}]" for i in range(len(normalized_videos))])
                audio_concat = f"{audio_streams}concat=n={len(normalized_videos)}:v=0:a=1:unsafe=1[aout]"
                filter_complex += audio_concat
                log_info(f"[转场合并] 音频合并: {audio_concat}")
                
                final_video = f"[vtmp{len(normalized_videos)-1}]"
                final_audio = "[aout]"  # 使用合并后的音频
                transition_applied = True
                merge_method = "filter_complex_with_transition"
                log_info(f"[转场合并] 最终视频输出: {final_video}, 音频输出: {final_audio}")
            else:
                log_info("[转场合并] 使用简单concat滤镜...")
                # 使用简单concat
                v_streams = "".join([f"[v{i}]" for i in range(len(normalized_videos))])
                a_streams = "".join([f"[a{i}]" for i in range(len(normalized_videos))])
                
                video_concat = f"{v_streams}concat=n={len(normalized_videos)}:v=1:a=0:unsafe=1[vout];"
                audio_concat = f"{a_streams}concat=n={len(normalized_videos)}:v=0:a=1:unsafe=1[aout];"
                
                filter_complex += video_concat + audio_concat
                log_info(f"[转场合并] 视频串联: {video_concat}")
                log_info(f"[转场合并] 音频串联: {audio_concat}")
                
                final_video = "[vout]"
                final_audio = "[aout]"
                merge_method = "filter_complex_concat"
            
            # 完整记录filter_complex表达式
            log_info(f"[转场合并] 完整filter_complex表达式: {filter_complex}")
            
            # 确保命令结构正确
            cmd.extend([
                "-filter_complex", filter_complex,
                "-map", final_video, 
                "-map", final_audio,
                "-c:v", "libx264",
                "-pix_fmt", "yuv420p",  # 显式指定像素格式
                "-profile:v", "main",   # 更兼容的配置文件
                "-level", "4.0",
                "-preset", "medium", 
                "-crf", "23",
                "-c:a", "aac",
                "-b:a", "192k",
                "-ar", "44100",
                "-movflags", "+faststart",  # 优化流媒体播放
                "-max_muxing_queue_size", "1024",  # 处理复杂拼接时避免队列溢出
                "-y",
                output_path
            ])
            
            # 执行命令并详细记录
            log_info(f"[转场合并] 开始执行ffmpeg合并命令...", highlight=True)
            self._run_ffmpeg(cmd, "视频过渡合并")
            
            log_info(f"[转场合并] 合并成功完成: {output_path}", highlight=True)
            
            # 如果需要移除人声
            if remove_voice:
                log_info("[转场合并] 开始移除人声...")
                output_path = self.separate_audio_from_video(output_path)
            
        except Exception as e:
            # 捕获并记录详细错误信息
            import traceback
            error_details = traceback.format_exc()
            transition_applied = False
            log_error(f"[转场合并] 视频合并失败，错误类型: {type(e).__name__}")
            log_error(f"[转场合并] 错误信息: {str(e)}")
            log_error(f"[转场合并] 详细错误栈:\n{error_details}")
            
            # 分析可能的错误原因
            error_str = str(e).lower()
            error_reason = "未知错误"
            if "no such filter" in error_str:
                error_reason = "ffmpeg版本不支持指定的滤镜"
            elif "invalid argument" in error_str:
                error_reason = "filter_complex表达式参数错误"
            elif "does not contain" in error_str:
                error_reason = "视频/音频流索引错误"
            elif "main profile doesn't support" in error_str:
                error_reason = "视频像素格式与profile不兼容"
            elif "pixel format" in error_str:
                error_reason = "像素格式不兼容"
            elif "error while decoding" in error_str:
                error_reason = "视频解码错误，可能是文件损坏"
            elif "FileNotFoundError" == type(e).__name__:
                error_reason = "FFMPEG没有安装"
                
            # 创建一个更具描述性的错误消息
            descriptive_error = f"视频合并失败: {error_reason}. 详细信息: {str(e)}. 详细日志请到日志文件查看。"
            self._log_error(f"[转场合并] {descriptive_error}")
            
            # 重新抛出异常，但附带更有用的错误信息
            raise RuntimeError(descriptive_error) from e

        finally:
            # 获取输出文件的基本信息
            file_size_mb = "未知"
            video_duration = "未知"
            if os.path.exists(output_path):
                file_size_mb = round(os.path.getsize(output_path) / (1024 * 1024), 2)
                try:
                    probe_cmd = [
                        "ffprobe", 
                        "-v", "error", 
                        "-show_entries", "format=duration", 
                        "-of", "csv=p=0", 
                        output_path
                    ]
                    video_duration = round(float(subprocess.check_output(probe_cmd, startupinfo=self.startupinfo).decode().strip()), 2)
                except:
                    pass
            
            # 详细的处理总结日志
            log_info("=" * 50, highlight=True)
            log_info(f"[合并总结] 视频合并完成", highlight=True)
            log_info(f"[合并总结] 处理视频数量: {len(video_paths)}")
            log_info(f"[合并总结] 合并方法: {merge_method}")
            log_info(f"[合并总结] 过渡效果: {self.transition_effect if transition_applied else '无'}")
            if transition_applied:
                log_info(f"[合并总结] 过渡持续时间: {self.transition_duration}秒")
            log_info(f"[合并总结] 移除人声: {remove_voice}")
            log_info(f"[合并总结] 输出文件: {output_path}")
            log_info(f"[合并总结] 文件大小: {file_size_mb} MB")
            log_info(f"[合并总结] 视频时长: {video_duration} 秒")
            log_info("=" * 50, highlight=True)
            
            self.cleanup()
            return transition_applied
            
    def _run_ffmpeg(self, cmd, operation_name, check=True):
        """
        执行ffmpeg命令并记录日志
        
        :param cmd: ffmpeg命令列表
        :param operation_name: 操作描述
        :param check: 是否检查返回码
        :return: subprocess.CompletedProcess对象
        :raises: RuntimeError: 当命令执行失败且check=True时
        """
        from src.utils.logger import log_ffmpeg
        
        try:
            # 捕获输出
            process = subprocess.run(
                cmd, 
                startupinfo=self.startupinfo,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
                encoding='latin-1',      # 使用latin-1编码，可以处理任何字节
                check=False  # 自己处理错误
            )
            
            if process.returncode == 0:
                log_ffmpeg(cmd, operation_name, success=True, debug=True)
                return process
            else:
                # 记录失败详情
                log_ffmpeg(cmd, operation_name, success=False)
                self._log_error(f"[FFMPEG错误] {operation_name}失败，返回码：{process.returncode}")
                
                # 记录错误输出（对排查至关重要）
                error_message = ""
                if process.stderr:
                    error_lines = process.stderr.split('\n')
                    filtered_errors = [line for line in error_lines if "error" in line.lower()]
                    
                    if filtered_errors:
                        error_detail = '|'.join(filtered_errors)
                        self._log_error(f"[FFMPEG错误详情] {error_detail}")
                        error_message = error_detail
                    else:
                        # 如果没有明确错误行，记录全部输出（但限制长度）
                        error_output = process.stderr
                        if len(error_output) > 1000:
                            error_output = error_output[:500] + "...\n[输出过长已截断]...\n" + error_output[-500:]
                        self._log_error(f"[FFMPEG完整错误输出] {error_output}")
                        error_message = error_output[:200] + "..." if len(error_output) > 200 else error_output
                
                if check:
                    # 使用RuntimeError而不是CalledProcessError，提供更清晰的错误信息
                    raise RuntimeError(f"FFMPEG操作 '{operation_name}' 失败: {error_message}")
                return process
                
        except Exception as e:
            self._log_error(f"[FFMPEG执行异常] {operation_name}: {str(e)}")
            if check:
                # 确保错误被传播
                if not isinstance(e, RuntimeError):
                    raise RuntimeError(f"FFMPEG操作 '{operation_name}' 发生异常: {str(e)}")
                raise
            return None


    def _analyze_audio_levels(self, file_path, media_processor):
        """分析音频文件的音量特性，返回分贝值
        
        Args:
            file_path: 音频文件路径
            media_processor: MediaProcessor实例，用于日志记录
        
        Returns:
            dict: 包含平均音量和最大音量的字典，或None（如果分析失败）
        """
        try:
            media_processor.log_info(f"分析音频音量特性: {os.path.basename(file_path)}")
            
            # 运行volumedetect滤镜分析音频
            cmd = [
                "ffmpeg",
                "-i", file_path,
                "-filter:a", "volumedetect",
                "-f", "null",
                "-y",
                "-"
            ]
            
            process = subprocess.run(
                cmd,
                startupinfo=self.startupinfo,
                stderr=subprocess.PIPE,
                stdout=subprocess.PIPE,
                universal_newlines=True,
                encoding='utf-8',
                errors='replace'
            )
            
            # 解析输出获取音量信息
            output = process.stderr
            mean_volume = None
            max_volume = None
            
            for line in output.split('\n'):
                if "mean_volume:" in line:
                    try:
                        mean_volume = float(line.split("mean_volume:")[1].strip().split()[0])
                    except (ValueError, IndexError):
                        pass
                elif "max_volume:" in line:
                    try:
                        max_volume = float(line.split("max_volume:")[1].strip().split()[0])
                    except (ValueError, IndexError):
                        pass
            
            result = {
                'mean_volume': mean_volume,
                'max_volume': max_volume
            }
            
            media_processor.log_info(f"音频分析结果 - 平均音量: {mean_volume} dB, 最大音量: {max_volume} dB")
            return result
            
        except Exception as e:
            media_processor.log_error(f"分析音频音量时出错: {str(e)}")
            return None

    def merge_video_with_audio(self, video_path, output_path, audio_path, subtitle_path=None, burn_subtitles=True, audio_volume=1.0, original_volume=0.8):
        """
        合并视频、音频和字幕文件，智能调整音量以确保人声清晰可闻
        
        :param video_path: 视频文件路径（含背景音乐）
        :param audio_path: 音频文件路径（含人声）
        :param output_path: 输出文件路径
        :param subtitle_path: 字幕文件路径 (可选)
        :param burn_subtitles: 是否将字幕烧入视频 (默认True)
        :param audio_volume: 新添加的音频默认音量倍数 (将被动态调整)
        :param original_volume: 原始视频音频默认音量倍数 (将被动态调整)
        :return: 处理结果
        """
        media_processor = MediaProcessor(startupinfo=self.startupinfo, logger=self.logger)
        
        # 基本文件验证
        if not os.path.exists(video_path):
            return {"success": False, "message": f"视频文件不存在: {video_path}"}
            
        if not os.path.exists(audio_path):
            return {"success": False, "message": f"音频文件不存在: {audio_path}"}
        
        # 检查字幕文件是否存在
        has_subtitle = False
        if subtitle_path and os.path.exists(subtitle_path):
            has_subtitle = True
            media_processor.log_info(f"找到字幕文件: {subtitle_path}")
        elif subtitle_path:
            media_processor.log_error(f"警告：字幕文件不存在: {subtitle_path}，将忽略字幕")
            subtitle_path = None
        
        # 验证并创建输出目录
        try:
            output_dir = os.path.dirname(output_path)
            if output_dir and not os.path.exists(output_dir):
                media_processor.log_info(f"创建输出目录: {output_dir}")
                os.makedirs(output_dir, exist_ok=True)
                
            # 测试目录写入权限
            if not os.access(output_dir, os.W_OK):
                media_processor.log_error(f"无写入权限: {output_dir}")
                return {"success": False, "message": f"无法写入输出目录: {output_dir}"}
        except Exception as e:
            media_processor.log_error(f"准备输出目录时出错: {e}")
            return {"success": False, "message": f"准备输出目录时出错: {e}"}
        
        try:
            # 获取视频和音频的基本信息
            video_duration = media_processor.get_media_duration(video_path)
            if video_duration is None:
                return {"success": False, "message": f"无法获取视频时长: {video_path}"}
                
            audio_duration = media_processor.get_media_duration(audio_path)
            if audio_duration is None:
                return {"success": False, "message": f"无法获取音频时长: {audio_path}"}
            
            # 检查原视频是否有音频轨道
            has_original_audio = media_processor.has_audio_stream(video_path)
            media_processor.log_info(f"原始视频包含音频轨道: {'是' if has_original_audio else '否'}")
            
            adjuster = DurationAdjuster(startupinfo=self.startupinfo, logger=self.logger)
            
            # 创建临时目录
            temp_dir = self.file_manager.create_temp_dir().path
            os.makedirs(temp_dir, exist_ok=True)
            
            # 调整时长
            adjusted = adjuster.handle_duration_mismatch(video_path, 
                                                         audio_path,
                                                         temp_dir,
                                                         transition_effect=self.transition_effect,
                                                         )
            
            if adjusted["adjusted"]:
                media_processor.log_info(f"时长调整成功，方法: {adjusted['method']}")
                # 使用调整后的文件
                video_path = adjusted["video"]
                audio_path = adjusted["audio"]
                
                # 重新获取调整后的时长信息
                video_duration = media_processor.get_media_duration(video_path)
                audio_duration = media_processor.get_media_duration(audio_path)
            # 如果没有调整时长但需要添加淡出效果
            else:
                # 应用单独的淡出效果
                video_result = self.apply_fade_effects(video_path, temp_dir)
                
                if video_result["success"]:
                    video_path = video_result["output_path"]
                    media_processor.log_info(f"添加淡出效果成功")
                else:
                    media_processor.log_warning(f"添加淡出效果失败: {video_result['message']}")

            # Add this to merge_video_with_audio function, right after getting durations
            media_processor.log_info(f"时长对比: 视频={video_duration:.2f}秒, 音频={audio_duration:.2f}秒, 差值={video_duration-audio_duration:.2f}秒")

            if video_duration > audio_duration:
                media_processor.log_info(f"视频时长大于音频，尝试加速视频")
            elif audio_duration > video_duration:
                media_processor.log_info(f"音频时长大于视频，尝试裁剪音频")
            else:
                media_processor.log_info(f"视频和音频时长匹配，无需调整")

            # 分析音频音量并智能计算音量参数
            voice_audio_info = self._analyze_audio_levels(audio_path, media_processor)
            adjusted_audio_volume = audio_volume  # 默认值
            adjusted_original_volume = original_volume  # 默认值
            
            if has_original_audio:
                # 分析视频中的音乐音量
                video_audio_info = self._analyze_audio_levels(video_path, media_processor)
                
                if voice_audio_info and video_audio_info:
                    voice_mean = voice_audio_info.get('mean_volume')
                    music_mean = video_audio_info.get('mean_volume')
                    
                    if voice_mean is not None and music_mean is not None:
                        # 计算音量差异（正值表示音乐比人声大）
                        volume_diff = music_mean - voice_mean
                        media_processor.log_info(f"音量差异分析: 音乐({music_mean}dB) vs 人声({voice_mean}dB), 差值: {volume_diff}dB")
                        
                        # 根据分析结果调整音量参数
                        if voice_mean < -40:  # 人声极弱
                            if volume_diff > 10:  # 音乐远大于人声
                                adjusted_audio_volume = 4.0  # 大幅提高人声
                                adjusted_original_volume = 0.1  # 大幅降低音乐
                                media_processor.log_info("情况1: 人声极弱且音乐远大于人声 - 大幅提高人声，大幅降低音乐")
                            else:
                                adjusted_audio_volume = 3.5
                                adjusted_original_volume = 0.15
                                media_processor.log_info("情况2: 人声极弱但音乐不太强 - 较大幅提高人声，较大幅降低音乐")
                        
                        elif voice_mean < -30:  # 人声较弱
                            if volume_diff > 10:
                                adjusted_audio_volume = 3.0
                                adjusted_original_volume = 0.2
                                media_processor.log_info("情况3: 人声较弱且音乐远大于人声 - 显著提高人声，显著降低音乐")
                            else:
                                adjusted_audio_volume = 2.5
                                adjusted_original_volume = 0.25
                                media_processor.log_info("情况4: 人声较弱但音乐不太强 - 适当提高人声，适当降低音乐")
                        
                        elif voice_mean < -20:  # 人声中等
                            if volume_diff > 5:
                                adjusted_audio_volume = 2.0
                                adjusted_original_volume = 0.3
                                media_processor.log_info("情况5: 人声中等且音乐略大于人声 - 中度提高人声，中度降低音乐")
                            else:
                                adjusted_audio_volume = 1.8
                                adjusted_original_volume = 0.4
                                media_processor.log_info("情况6: 人声中等且音乐与人声相近 - 轻度提高人声，轻度降低音乐")
                        
                        else:  # 人声较强
                            if music_mean > -15:  # 音乐也很强
                                adjusted_audio_volume = 1.5
                                adjusted_original_volume = 0.5
                                media_processor.log_info("情况7: 人声较强但音乐也很强 - 略微提高人声，适度降低音乐")
                            else:  # 音乐较弱
                                adjusted_audio_volume = 1.3
                                adjusted_original_volume = 0.6
                                media_processor.log_info("情况8: 人声较强且音乐较弱 - 保持平衡，略微调整")
                        
                        # 如果音乐极强（>-10dB），需要额外降低背景音乐
                        if music_mean > -10:
                            adjusted_original_volume *= 0.7
                            media_processor.log_info(f"音乐极强(>{music_mean}dB)，额外降低背景音乐音量")
                        
                        # 如果人声极弱（<-45dB），需要额外提高人声
                        if voice_mean < -45:
                            adjusted_audio_volume *= 1.3
                            media_processor.log_info(f"人声极弱(<{voice_mean}dB)，额外提高人声音量")
                        
                        media_processor.log_info(f"智能调整后的音量参数: 人声={adjusted_audio_volume}, 背景音乐={adjusted_original_volume}")
                    else:
                        media_processor.log_warning("无法获取完整的音量信息，使用默认音量参数")
                else:
                    media_processor.log_warning("音频分析失败，使用默认音量参数")
            else:
                # 没有背景音乐，只需调整人声音量
                if voice_audio_info and voice_audio_info.get('mean_volume'):
                    voice_mean = voice_audio_info.get('mean_volume')
                    
                    # 根据人声音量调整
                    if voice_mean < -40:
                        adjusted_audio_volume = 3.0
                        media_processor.log_info(f"人声极弱({voice_mean}dB)，提高音量至3.0")
                    elif voice_mean < -30:
                        adjusted_audio_volume = 2.0
                        media_processor.log_info(f"人声较弱({voice_mean}dB)，提高音量至2.0")
                    elif voice_mean < -20:
                        adjusted_audio_volume = 1.5
                        media_processor.log_info(f"人声适中({voice_mean}dB)，提高音量至1.5")
                    else:
                        adjusted_audio_volume = 1.2
                        media_processor.log_info(f"人声较强({voice_mean}dB)，保持适当音量1.2")
                        
            # 构建ffmpeg命令
            cmd = [
                "ffmpeg",
                "-i", video_path,  # 视频输入
                "-i", audio_path,  # 音频输入
            ]
            
            # 准备滤镜表达式
            filters = []
            
            # 音频处理部分 - 使用优化后的音量参数
            if has_original_audio:
                # 处理原始音频音量
                filters.append(f"[0:a]volume={adjusted_original_volume}[original_audio]")
                
                # 处理新音频 - 如果需要填充
                if audio_duration < video_duration:
                    padding_seconds = video_duration - audio_duration
                    media_processor.log_info(f"音频比视频短 {padding_seconds:.2f}秒，将自动添加静音填充")
                    filters.append(f"[1:a]apad=whole_dur={video_duration},volume={adjusted_audio_volume}[new_audio]")
                else:
                    filters.append(f"[1:a]volume={adjusted_audio_volume}[new_audio]")
                
                # 混合两个音频轨道
                filters.append("[original_audio][new_audio]amix=inputs=2:duration=longest[mixed_audio]")
                
                # 处理字幕滤镜
                if has_subtitle:
                    # 修复字幕路径转义问题
                    subtitle_escaped = subtitle_path.replace('\\', '/').replace(':', '\\\\:')
                    subtitle_filter = f"[0:v]subtitles={subtitle_escaped}:force_style='FontSize=12,MarginV=20'[v]"
                    filters.append(subtitle_filter)
                    
                    # 构建完整的filter_complex表达式
                    filter_complex = ";".join(filters)
                    
                    # 添加映射
                    cmd.extend([
                        "-filter_complex", filter_complex,
                        "-map", "[v]",
                        "-map", "[mixed_audio]"
                    ])
                    
                    # 指定编码器
                    cmd.extend([
                        "-c:v", "libx264",
                        "-preset", "medium",
                        "-crf", "18"
                    ])
                else:
                    # 没有字幕，只处理音频混合
                    filter_complex = ";".join(filters)
                    cmd.extend([
                        "-filter_complex", filter_complex,
                        "-map", "0:v",
                        "-map", "[mixed_audio]"
                    ])
                    
                    # 没有视频滤镜时可以直接复制视频流
                    cmd.extend(["-c:v", "copy"])
            else:
                # 原始视频没有音频轨道，直接使用新音频
                media_processor.log_info("原始视频没有音频轨道，直接使用新音频")
                
                # 音频填充和音量调整
                audio_filters = []
                
                if audio_duration < video_duration:
                    audio_filters.append(f"apad=whole_dur={video_duration}")
                    media_processor.log_info(f"音频比视频短 {video_duration - audio_duration:.2f}秒，添加静音填充")
                
                audio_filters.append(f"volume={adjusted_audio_volume}")
                
                # 如果有字幕
                if has_subtitle:
                    subtitle_escaped = subtitle_path.replace('\\', '/').replace(':', '\\\\:')
                    subtitle_filter = f"subtitles={subtitle_escaped}:force_style='FontSize=12,MarginV=20'"
                    
                    cmd.extend([
                        "-vf", subtitle_filter,
                        "-c:v", "libx264",
                        "-preset", "medium",
                        "-crf", "18"
                    ])
                else:
                    cmd.extend(["-c:v", "copy"])
                
                # 添加音频滤镜
                if audio_filters:
                    cmd.extend(["-af", ",".join(audio_filters)])
                
                # 基本映射
                cmd.extend([
                    "-map", "0:v:0",
                    "-map", "1:a:0",
                ])
            
            # 完成命令
            cmd.extend([
                "-y",
                output_path
            ])
            
            # 记录完整命令
            media_processor.log_info(f"FFMPEG完整命令: {' '.join(cmd)}")
            
            # 执行命令
            result = self._run_ffmpeg(cmd, "智能音量混合")
            
            # 验证输出文件
            if os.path.exists(output_path):
                output_size_mb = round(os.path.getsize(output_path) / (1024 * 1024), 2)
                media_processor.log_info(f"输出文件大小: {output_size_mb} MB")
                
                # 检查输出文件的音轨数量
                audio_streams = media_processor.get_audio_streams_count(output_path)
                media_processor.log_info(f"输出视频包含 {audio_streams} 个音频轨道")
                
                return {
                    "success": True, 
                    "message": f"视频、{('原音频+新音频' if has_original_audio else '新音频')}"+
                            f"{('和字幕(硬字幕)' if has_subtitle else '')} 合并成功 (智能音量调整)", 
                    "output_path": output_path
                }
            else:
                error_message = "处理完成但输出文件不存在"
                media_processor.log_error(error_message)
                return {"success": False, "message": error_message}
                
        except Exception as e:
            error_message = f"合并视频、音频和字幕时出错: {e}"
            media_processor.log_error(error_message)
            import traceback
            media_processor.log_error(traceback.format_exc())
            return {"success": False, "message": error_message}
        
    def apply_fade_effects(self, video_path, temp_dir, fade_duration=1.0):
        """
        为视频添加淡入淡出效果
        
        :param video_path: 视频文件路径
        :param temp_dir: 临时目录路径
        :param fade_duration: 淡出效果持续时间（秒）
        :return: 处理结果，包含输出路径
        """
        # 确保输出路径包含原始视频的文件扩展名
        file_basename = os.path.basename(video_path)
        name, ext = os.path.splitext(file_basename)
        
        # 创建输出路径，确保保留原始文件扩展名
        output_path = os.path.join(temp_dir, f"fade_out_{name}{ext}")
        
        log_info(f"添加淡出效果: 输入={file_basename}, 输出={os.path.basename(output_path)}")
        
        # 使用FadeProcessor应用淡出效果
        fade_processor = FadeProcessor(startupinfo=self.startupinfo, logger=self.logger)
        result = fade_processor.apply_video_fade_out(
            video_path=video_path,
            output_path=output_path,
            fade_duration=fade_duration
        )
        
        if not result["success"]:
            self._log_error(f"应用淡出效果失败: {result.get('message', '未知错误')}")
        else:
            log_info(f"成功添加淡出效果: {os.path.basename(output_path)}")
        
        return result
    
    
    def burn_subtitles_to_video(self, video_path, subtitle_path, output_path=None, 
                           font_size=10, margin_v=25, font_color="white", font_name=None):
        """
        将字幕烧录到视频中
        
        :param video_path: 视频文件路径
        :param subtitle_path: 字幕文件路径（SRT格式）
        :param output_path: 输出文件路径，如果为None则自动生成
        :param font_size: 字体大小
        :param margin_v: 垂直边距
        :param font_color: 字体颜色
        :param font_name: 字体名称，不指定则使用默认字体
        :return: 处理结果字典
        """
        media_processor = MediaProcessor(startupinfo=self.startupinfo, logger=self.logger)
        
        # 基本文件验证
        if not os.path.exists(video_path):
            return {"success": False, "message": f"视频文件不存在: {video_path}"}
            
        if not os.path.exists(subtitle_path):
            return {"success": False, "message": f"字幕文件不存在: {subtitle_path}"}
        
        # 如果未提供输出路径，生成一个
        if output_path is None:
            file_basename = os.path.basename(video_path)
            name, ext = os.path.splitext(file_basename)
            temp_dir = self.file_manager.create_temp_dir().path
            os.makedirs(temp_dir, exist_ok=True)
            output_path = os.path.join(temp_dir, f"{name}_with_subs{ext}")
        
        try:
            # 确保输出目录存在
            output_dir = os.path.dirname(output_path)
            if output_dir and not os.path.exists(output_dir):
                os.makedirs(output_dir, exist_ok=True)
            
            # 修复字幕路径中的特殊字符
            subtitle_escaped = subtitle_path.replace('\\', '/').replace(':', '\\\\:')
            
            # 构建字体样式
            style = f"FontSize={font_size},MarginV={margin_v}"
            if font_color:
                style += f",PrimaryColour=&H{self._color_to_hex(font_color)}"
            if font_name:
                style += f",FontName={font_name}"
                
            media_processor.log_info(f"开始烧录字幕: {os.path.basename(subtitle_path)} -> {os.path.basename(video_path)}")
            media_processor.log_info(f"字体样式: {style}")
            
            # 构建命令
            cmd = [
                "ffmpeg",
                "-i", video_path,
                "-vf", f"subtitles={subtitle_escaped}:force_style='{style}'",
                "-c:v", "libx264",
                "-preset", "medium",
                "-crf", "18",
                "-c:a", "copy",  # 保留原始音频
                "-y",
                output_path
            ]
            
            # 执行命令
            result = self._run_ffmpeg(cmd, "字幕烧录")
            
            # 检查结果
            if os.path.exists(output_path):
                file_size = os.path.getsize(output_path) / (1024 * 1024)  # MB
                duration = media_processor.get_media_duration(output_path)
                
                media_processor.log_info(f"字幕烧录成功: {os.path.basename(output_path)}")
                media_processor.log_info(f"输出文件大小: {file_size:.2f} MB, 时长: {duration:.2f}秒")
                
                return {
                    "success": True,
                    "message": "字幕烧录成功",
                    "output_path": output_path,
                    "file_size_mb": round(file_size, 2),
                    "duration": duration
                }
            else:
                error_message = "处理完成但输出文件不存在"
                media_processor.log_error(error_message)
                return {"success": False, "message": error_message}
                
        except Exception as e:
            error_message = f"烧录字幕时出错: {str(e)}"
            media_processor.log_error(error_message)
            import traceback
            media_processor.log_error(traceback.format_exc())
            return {"success": False, "message": error_message}
        
    def _color_to_hex(self, color):
        """将常见颜色名称转换为十六进制RGB颜色"""
        color_map = {
            "white": "FFFFFF",
            "black": "000000",
            "red": "FF0000",
            "green": "00FF00",
            "blue": "0000FF",
            "yellow": "FFFF00",
            "cyan": "00FFFF",
            "magenta": "FF00FF"
        }
        
        # 如果是预定义颜色名称
        if color.lower() in color_map:
            return color_map[color.lower()]
        
        # 如果是十六进制颜色代码
        if color.startswith('#') and len(color) in [4, 7]:
            return color[1:]
        
        # 默认返回白色
        return "FFFFFF"